﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using SeguradoraIA.Diagrama;
using SeguradoraIA.Class;

namespace SeguradoraIA.Business
{
    public class RedeNeural
    {
        public TimeSpan TempoProcessamento { get; set; }
        public int Iteracoes { get; set; }
        public List<String> ListaClassificacao { get; set; }
        public List<Peso> ListaPeso { get; set; }
        SeguradoraIAEntities ctx = new SeguradoraIAEntities();

        public string TreinarRede()
        {
            string retorno = string.Empty;
            ListaClassificacao = new List<string>();
            ListaPeso = new List<Peso>();
            TempoProcessamento = DateTime.Now.TimeOfDay;
            Iteracoes = 0;
            int testeOK;

            List<double> listaPeso = this.RecuperarListaPeso();
            List<Entrada> listaEntrada = this.RecuperaEntrada();

            if (listaEntrada.Count() > 0)
            {
                do
                {
                    testeOK = 0;

                    #region - TREINAR -
                    for (int i = 0; i < listaEntrada.Count(); i++)
                    {
                        Iteracoes++;
                        listaPeso = this.Treinar(listaEntrada[i].Resultado, 0.4, listaPeso, listaEntrada[i].ListaEntrada);
                    }
                    #endregion

                    #region - TESTAR -
                    foreach (var item in listaEntrada)
                    {
                        Boolean result = this.Testar(listaPeso, item.ListaEntrada, item.Resultado);

                        if (!result)
                            break;
                        else
                            testeOK++;
                    }
                    #endregion

                } while (testeOK < listaEntrada.Count());

                #region - CLASSIFICAR -
                foreach (var item in listaEntrada)
                {
                    this.Classificar(item.ListaEntrada);
                }
                #endregion

                ctx = new SeguradoraIAEntities();

                Peso peso = ctx.Peso.FirstOrDefault();

                peso.PesoBairro = listaPeso[0];
                peso.PesoCursoFormacao = listaPeso[1];
                peso.PesoGrauInstrucao = listaPeso[2];
                peso.PesoIdade = listaPeso[3];
                peso.PesoMarcaCarro = listaPeso[4];
                peso.PesoProfissao = listaPeso[5];
                peso.PesoSinistro = listaPeso[6];
                peso.PesoTempoHabilitacao = listaPeso[7];
                peso.PesoTipoCarro = listaPeso[8];
                peso.PesoValorCarro = listaPeso[9];

                ctx.SaveChanges();

                retorno = "Rede treinada com sucesso!";
            }
            else
                retorno = "Não existem novos perfis para treinar a rede";

            TempoProcessamento = DateTime.Now.TimeOfDay.Subtract(TempoProcessamento);

            return retorno;
        }

        private List<Entrada> RecuperaEntrada()
        {
            int idSelecione = EnumDomain.ResultadoEsperadoBDEnum.Selecione.GetHashCode();

            List<Perfil> listaPerfil = ctx.Perfil.Where(x => x.IdResultadoEsperado != idSelecione).ToList();

            listaPerfil.ForEach(
                x =>
                {
                    if (x.IdResultadoEsperado == EnumDomain.ResultadoEsperadoBDEnum.AltoRisco.GetHashCode())
                        x.IdResultadoEsperado = EnumDomain.ResultadoEsperadoEnum.AltoRisco.GetHashCode();
                    else if (x.IdResultadoEsperado == EnumDomain.ResultadoEsperadoBDEnum.BaixoRisco.GetHashCode())
                        x.IdResultadoEsperado = EnumDomain.ResultadoEsperadoEnum.BaixoRisco.GetHashCode();
                });

            Entrada entrada = new Entrada();

            return entrada.RetornaEntrada(listaPerfil);
        }

        public List<double> RecuperarListaPeso()
        {
            Peso peso = ctx.Peso.FirstOrDefault();

            List<double> listaPeso = new List<double>()
            {
                peso.PesoBairro,
                peso.PesoCursoFormacao,
                peso.PesoGrauInstrucao,
                peso.PesoIdade,
                peso.PesoMarcaCarro,
                peso.PesoProfissao,
                peso.PesoSinistro,
                peso.PesoTempoHabilitacao,
                peso.PesoTipoCarro,
                peso.PesoValorCarro,
            };
            return listaPeso;
        }

        #region - Metodos de Calculo -

        public int Classificar(List<double> listaEntrada)
        {
            List<double> listaPeso = this.RecuperarListaPeso();
            Double u = 0;

            for (int i = 0; i < listaPeso.Count; i++)
            {
                u += listaPeso[i] * listaEntrada[i];
            }

            if (u >= 0)
                return EnumDomain.ResultadoEsperadoEnum.AltoRisco.GetHashCode();
            else
                return EnumDomain.ResultadoEsperadoEnum.BaixoRisco.GetHashCode();
        }

        private bool Testar(List<double> listaPeso, List<double> listaEntrada, double resultadoDesejado)
        {
            Double u = 0;
            Double resultado = 0;
            for (int i = 0; i < listaPeso.Count; i++)
            {
                u += listaPeso[i] * listaEntrada[i];
            }

            if (u >= 0)
                resultado = 1;
            else
                resultado = -1;

            return resultadoDesejado == resultado;
        }

        private List<double> Treinar(double resultadoDesejado, double taxaAprendizagem, List<double> listaPeso, List<double> listaEntrada)
        {
            double u = 0;
            double resultadoAnt = 0;
            for (int i = 0; i < listaPeso.Count; i++)
            {
                u += listaPeso[i] * listaEntrada[i];
            }

            if (u >= 0)
                resultadoAnt = 1;
            else
                resultadoAnt = -1;

            if (!resultadoDesejado.Equals(resultadoAnt))
            {
                for (int i = 0; i < listaPeso.Count; i++)
                {
                    listaPeso[i] = listaPeso[i] + (taxaAprendizagem * (resultadoDesejado - resultadoAnt)) * listaEntrada[i];
                }

                listaPeso = Treinar(resultadoDesejado, taxaAprendizagem, listaPeso, listaEntrada);
            }

            return listaPeso;
        }

        #endregion
    }
}